
/*
 * parser.c
 *
 * vt500 series parser
 *
 *
 * 14.05.2010, Klaus Hennemann
 *
 */

#include <string.h>
#include <stdio.h>
#include "parser.h"


/* 
 * \brief Clears the intermediate and parameter buffers 
 *
 * \param d             pointer of the parser data
 */
static void parser_clear (struct ParserData *d)
{
        d->parm_count = 0;
        d->parm_overflow = 0;

	*d->imed_buf = 0;
        d->imed_len = 0;
        d->imed_overflow = 0;
}


/* 
 * \brief Collect intermediate characters 
 *
 * \param d             pointer of the parser data
 * \param uc            character to collect
 */
static inline void parser_collect (struct ParserData *d, Betty_Character uc)
{
	/* we have to 0-terminate the intermediate buffer, so we
	   have to check if two characters fit into the remaining
	   buffer space. If not, we have an overflow */
        if (d->imed_len > sizeof(d->imed_buf) - 2) {
                d->imed_overflow = 1;
                return;
        } 
        
        d->imed_buf[d->imed_len] = uc;
        d->imed_len++;

	/* 0-terminate current intermediate string */
	d->imed_buf[d->imed_len] = 0;
}


static inline void parser_param (struct ParserData *d, Betty_Character uc)
{
        if (d->parm_overflow)
                return;

        if (uc == ';') {
                if (d->parm_count == 0) {
                        d->parm_value[0] = 0;
                        d->parm_value[1] = 0;
                        d->parm_count = 2;
                } else if (d->parm_count < PARSER_PARAM_MAX) {
                        d->parm_value[d->parm_count] = 0;
                        d->parm_count++;
                } else {
                        d->parm_overflow = 1;
                }

                return;
        }

        if (d->parm_count == 0) {
                d->parm_value[0] = uc - (unsigned char)'0';
                d->parm_count = 1;
        } else {
                d->parm_value[d->parm_count - 1] *= 10;
                d->parm_value[d->parm_count - 1] += uc - '0';
        }
}


/* 
 * \brief Performs a parser action
 *
 * \param p             pointer to the current parser
 * \param action        ID of action to perform
 * \param uc            received character that triggered the action
 */
static inline void parser_exec_action (struct Parser *p, enum ActionID action, 
                         Betty_Character uc)
{
        switch (action) {
        case ACTION_PRINT:
        case ACTION_EXECUTE:
        case ACTION_ESC_DISPATCH:
        case ACTION_CSI_DISPATCH:
        case ACTION_HOOK:
        case ACTION_PUT:
        case ACTION_UNHOOK:
        case ACTION_OSC_START:
        case ACTION_OSC_PUT:
        case ACTION_OSC_END:
                if (p->cb) {
                        p->data.final = uc;
                        p->data.action = action;
                        p->cb(p->cb_arg, &p->data);
                }
                break;

        case ACTION_PARAM:
                parser_param(&p->data, uc);                
                break;

        case ACTION_COLLECT:
                parser_collect(&p->data, uc);
                break;

        case ACTION_CLEAR:
                parser_clear(&p->data);
                break;

        default:
                break;
        }
}


/*--------------------------------------------------------------------------*/
/*	INTERFACE FUNCTIONS						    */
/*--------------------------------------------------------------------------*/


void parser_init (struct Parser *p, parser_callback_t cb, void *cb_arg)
{
        memset(p, 0, sizeof(*p));
        p->state = parser_state_tab[STATE_GROUND]; 
        p->cb = cb;
        p->cb_arg = cb_arg;

	parser_clear(&p->data);
}


void parser_cleanup (struct Parser *p)
{
        p->cb = 0;
}


void parser_in (struct Parser *p, Betty_Character uc)
{
        const struct Event *e;

        e = p->state->event + ((uc > 0xff) ? EVENT_WCHAR : uc);

        /* execute event based action */
        parser_exec_action(p, e->action, uc);

        /* state change ? */
        if (e->new_state != STATE_NULL && p->state->id != e->new_state) {
                parser_exec_action(p, p->state->exit, 0);
                p->state = parser_state_tab[e->new_state];
                parser_exec_action(p, p->state->entry, 0);
        }
}


#ifdef DEBUG
void parser_dump (const char *prefix, const struct ParserData *d)
{
	switch (d->action) {
	case ACTION_EXECUTE:
		printf("%s\\%04o #C0C1 %dd %02xh CTRL-%c\n", 
			prefix, d->final, d->final, d->final, d->final | 0x40);
		break;

	case ACTION_ESC_DISPATCH:
		printf("%s<ESC>%s%c\n", prefix, d->imed_buf, d->final);
		break;

	case ACTION_CSI_DISPATCH:
		printf("%s<ESC>[%s", prefix, d->imed_buf);
	
		if (d->parm_count > 0) {
			int n;

			printf("%d", d->parm_value[0]);

			for (n = 1; n < d->parm_count; n++)
				printf(";%d", d->parm_value[n]);
		}

		printf("%c", d->final);

		if (d->imed_overflow || d->parm_overflow) {
			printf(" #");

			if (d->imed_overflow)
				printf(" [imed overflow]");

			if (d->parm_overflow)
				printf(" [parm overflow]");
		}

		printf("\n");
		break;

	case ACTION_PRINT:
		printf("%s\\%04o # 0x%04x %c\n", 
			prefix, d->final & 0xff, d->final, d->final);
		break;

	default:
		break;

	}
}
#endif

